using B.S.NewMedical.Api.Write.Application.Command.Doctor;
using B.S.NewMedical.Domain.Appointment;
using B.S.NewMedical.ErrorCode;
using B.S.NewMedical.Infrastructure;
using B.S.NewMedical.Infrastructure.Interface;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace B.S.NewMedical.Api.Write.Application.Handler.Doctor
{
    /// <summary>
    /// 收费处理器
    /// </summary>
    public class ProcessChargeHandler : IRequestHandler<ProcessChargeCommand, ApiResult<int>>
    {
        private readonly IBaseRepository<MedicalCard> _medicalCardRepository;
        private readonly IBaseRepository<Visit> _visitRepository;
        private readonly IBaseRepository<Prescription> _prescriptionRepository;
        private readonly IBaseRepository<Laborder> _laborderRepository;
        private readonly IBaseRepository<Charge> _chargeRepository;
        private readonly MyDbcontext _dbContext;
        private readonly ILogger<ProcessChargeHandler> _logger;

        public ProcessChargeHandler(
            IBaseRepository<MedicalCard> medicalCardRepository,
            IBaseRepository<Visit> visitRepository,
            IBaseRepository<Prescription> prescriptionRepository,
            IBaseRepository<Laborder> laborderRepository,
            IBaseRepository<Charge> chargeRepository,
            MyDbcontext dbContext,
            ILogger<ProcessChargeHandler> logger)
        {
            _medicalCardRepository = medicalCardRepository;
            _visitRepository = visitRepository;
            _prescriptionRepository = prescriptionRepository;
            _laborderRepository = laborderRepository;
            _chargeRepository = chargeRepository;
            _dbContext = dbContext;
            _logger = logger;
        }

        public async Task<ApiResult<int>> Handle(ProcessChargeCommand request, CancellationToken cancellationToken)
        {
            using var transaction = await _dbContext.Database.BeginTransactionAsync(cancellationToken);
            try
            {
                _logger.LogInformation("开始处理收费，就诊卡号: {CardNo}，实收金额: {RealAmount}", request.CardNo, request.RealAmount);

                // 参数验证
                if (string.IsNullOrWhiteSpace(request.CardNo))
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "就诊卡号不能为空" };

                if (request.TotalAmount <= 0)
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "应收金额必须大于0" };

                if (request.RealAmount <= 0)
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "实收金额必须大于0" };

                if (string.IsNullOrWhiteSpace(request.PayChannel))
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "支付方式不能为空" };

                if (request.CashierId <= 0)
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "收费员ID不能为空" };

                // 验证支付金额
                if (request.CardPayAmount + request.OtherPayAmount != request.RealAmount)
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "医疗卡支付金额 + 其他支付金额必须等于实收金额" };

                if (request.CardPayAmount < 0 || request.OtherPayAmount < 0)
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "支付金额不能为负数" };

                // 验证就诊卡
                var medicalCard = await _medicalCardRepository.GetAll()
                    .FirstOrDefaultAsync(mc => mc.Card_no == request.CardNo && !mc.IsDeleted, cancellationToken);

                if (medicalCard == null)
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "就诊卡不存在" };

                if (medicalCard.Status != "正常")
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "就诊卡状态异常，无法使用" };

                // 验证医疗卡余额
                if (request.CardPayAmount > 0)
                {
                    if (medicalCard.Card_price < request.CardPayAmount)
                        return new ApiResult<int> { Code = ApiEnum.Fail, Msg = $"医疗卡余额不足，当前余额：{medicalCard.Card_price}元" };
                }

                // 获取最近的就诊记录作为关联
                var recentVisit = await _visitRepository.GetAll()
                    .Where(v => v.Patient_id == medicalCard.Patient_id && !v.IsDeleted)
                    .OrderByDescending(v => v.Visit_time)
                    .FirstOrDefaultAsync(cancellationToken);

                if (recentVisit == null)
                    return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "未找到就诊记录" };

                // 验证和更新处方状态
                if (request.PrescriptionIds.Any())
                {
                    var prescriptions = await _prescriptionRepository.GetAll()
                        .Where(p => request.PrescriptionIds.Contains(p.Id) && p.status == "待收费" && !p.IsDeleted)
                        .ToListAsync(cancellationToken);

                    if (prescriptions.Count != request.PrescriptionIds.Count)
                        return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "部分处方状态异常，无法收费" };

                    // 更新处方状态
                    foreach (var prescription in prescriptions)
                    {
                        prescription.status = "已发药";
                        await _prescriptionRepository.UpdateAsync(prescription);
                    }
                }

                // 验证和更新检验单状态
                if (request.LaborderIds.Any())
                {
                    var laborders = await _laborderRepository.GetAll()
                        .Where(l => request.LaborderIds.Contains(l.Id) && l.Status == "待收费" && !l.IsDeleted)
                        .ToListAsync(cancellationToken);

                    if (laborders.Count != request.LaborderIds.Count)
                        return new ApiResult<int> { Code = ApiEnum.Fail, Msg = "部分检验单状态异常，无法收费" };

                    // 更新检验单状态
                    foreach (var laborder in laborders)
                    {
                        laborder.Status = "已出结果";
                        await _laborderRepository.UpdateAsync(laborder);
                    }
                }

                // 扣除医疗卡余额
                if (request.CardPayAmount > 0)
                {
                    medicalCard.Card_price -= request.CardPayAmount;
                    await _medicalCardRepository.UpdateAsync(medicalCard);
                }

                // 创建收费记录
                var charge = new Charge
                {
                    Visit_id = recentVisit.Id,
                    Total_amount = request.TotalAmount,
                    Discount = request.Discount,
                    Real_amount = request.RealAmount,
                    Pay_channel = request.PayChannel,
                    Pay_time = DateTime.Now,
                    Cashier_id = request.CashierId,
                    status = "已收费",
                    IsDeleted = false
                };

                await _chargeRepository.CreateAsync(charge);

                await _dbContext.SaveChangesAsync(cancellationToken);
                await transaction.CommitAsync(cancellationToken);

                _logger.LogInformation("收费成功，收费记录ID: {ChargeId}，处方数: {PrescriptionCount}，检验单数: {LaborderCount}，医疗卡支付: {CardPayAmount}，其他支付: {OtherPayAmount}",
                    charge.Id, request.PrescriptionIds.Count, request.LaborderIds.Count, request.CardPayAmount, request.OtherPayAmount);

                return new ApiResult<int>
                {
                    Code = ApiEnum.Success,
                    Msg = "收费成功",
                    Data = charge.Id
                };
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync(cancellationToken);
                _logger.LogError(ex, "收费时发生错误，就诊卡号: {CardNo}", request.CardNo);
                return new ApiResult<int>
                {
                    Code = ApiEnum.Fail,
                    Msg = "收费失败"
                };
            }
        }
    }
} 